想像你負責一個支付系統的 API。某天凌晨,一個客戶的 server 因為網路不穩定,對同一筆 $50,000 USDC 的付款請求重送了三次。
在 Web2 世界中,最壞情況下你打電話給銀行,銀行就會幫你做沖正(reversal),然後錢就回來了(甚至理論上這都不會發生,銀行系統非常成熟)。
但在鏈上沒有人幫忙沖正這回事。transfer() 一旦上鏈確認,那筆錢就是別人的了。你唯一的防線,就是在交易離開你的系統之前就把它擋下來。
而「把交易擋下來」這件事,比想像中難得多。在一個真實的結算系統裡,一筆付款要穿過一整條不可靠的 pipeline:
於是,我們這個系列的核心命題只有一句話:
鏈下連結鏈上的結算系統,本質是在一個不可靠的非同步世界裡,保證錢只動一次。
所謂「只動一次」aka 「Exactly-once」指的是一個端到端的系統性質。它需要 API 層的 idempotency key、ledger 層的 double-entry 約束、queue 層的 retry 語義、合約層的防 replay 設計,加上對每條鏈 finality 模型的正確理解,全部同時成立才會出現。任何一層只要不穩定了,錢就會多動一次。
接下來 30 天,我們就來把這套系統一層一層蓋起來。
一個 prototype 級、但按 production 標準設計的穩定幣結算系統,涵蓋四個部分:
為什麼是這四條鏈?因為它們剛好代表了四種交易模型的極端:
| 帳戶模型 | 交易序列化 | 「成功」的定義 | |
|---|---|---|---|
| EVM | Account-based | Nonce | 同步、需等 finality |
| Solana | Account + Program | Blockhash expiry | 同步、confirmed/finalized 兩級 |
| TON | Actor model | Seqno | 非同步、交易成功需要完整 trace 驗證 |
| SUI | Object-based | Object version | 同步、但所有權模型顛覆合約設計 |
任何只在 EVM 上想出來的「通用設計」,遇到 TON 的 async message 都會當場解體,所以我希望能設計同時涵蓋這四者的一個抽象。
這是我希望最終完成時的系統全貌(未來可能改變):
注意三個貫穿全圖的東西。
第一是簽名迴圈(圖上的 3 個點)。系統收到 payment request 後先落地 intent,再把待簽的 payment payload 回傳給使用者;使用者用自託管錢包或託管服務的 API 完成簽名後回傳 signed payload,relayer 才以自己的錢包代付 gas 上鏈。這代表整套系統是 non-custodial 的:我們從頭到尾不持有客戶資產,只負責把「已授權的支付」穩定送上鏈(這是目前大家避開監管問題的首選方案)。而 gas 代付在四條鏈上的機制完全不同(EVM 要靠 Permit2 的 typed data 簽名、Solana 有原生 fee payer、SUI 有 sponsored transaction、TON 要用 wallet v5),這之後會深入討論。
第二是 PaymentRef。它從 API 進來的那一刻誕生,穿過 ledger、queue、relayer,最後被寫進鏈上交易(calldata / memo / event),再由 listener 撈回來交給對帳引擎的閉環。它是整個系統的 audit tracing key:任何一筆錢,都能從鏈上 tx hash 反查回最初的那個 API request。
第三是狀態的 SSOT (single truth of source) 存在於鏈下 ledger,鏈上只提供執行結果。這聽起來蠻違反 Web3 直覺的,但對企業級結算系統來說這是比較好的方案。我們之後會再討論這件事。
全系列的程式碼會放在公開 repo,每篇文章對應一個 git tag,所以你可以 checkout 到任何一天的狀態,看到當時系統的完整樣貌。
明天見。
關於本系列的聲明
本系列為個人 side project,在個人時間、個人設備上從零開發。所有文章內容與程式碼皆以公開文件、開源專案與業界通用設計模式為基礎現場推導,repo 的 commit history 完整記錄了每個設計從空白檔案長出來的過程。
本系列不代表任何僱主的立場,不包含任何來自僱主或客戶的內部資訊、程式碼、架構細節與營運數據。文中提及的所有公司、產品與數字,均以公開資料為準並附上來源。系統設計如與任何現存商業系統相似,屬於業界通用模式的自然收斂。